Option Explicit

' ======================================================================
' Descriptor
' 函数列对象
' ======================================================================

' ======================================================================
' Implement
' ======================================================================

Implements ISqlFormatterElem

' ======================================================================
' Field
' ======================================================================

' ==================================================
' Field
' ==================================================
' 函数名 token
Private funcNameToken As SqlToken
' 函数体
Private funcBody As ISqlFormatterElem

' ==================================================
' ISqlFormatterElem Field
' ==================================================
' 前缀空格的数量
' 默认值 = 0
Private prefixSpaceCount As Integer

' 左侧括号的数量
' 默认值 = 0
Private leftParenthesisCount As Integer
' 右侧括号的数量
' 默认值 = 0
Private rightParenthesisCount As Integer

' ======================================================================
' Public Function
' ======================================================================
' 使用函数名 token 进行初始化
Public Sub Init(ByRef funcName As SqlToken)
    Set funcNameToken = funcName
End Sub

' ======================================================================
' implements ISqlFormatterElem Function
' ======================================================================
' ==================================================
' ISqlFormatterElem -- 添加组成元素
' ==================================================
' 添加组成元素
Public Sub ISqlFormatterElem_addComponent(ByRef c As ISqlFormatterElem)
    If c IS Nothing Then
        Exit Sub
    End If

    ' 如果函数体不存在，则直接保存
    If funcBody Is Nothing Then
        Set funcBody = c
        Exit Sub
    End If

    ' 如果函数体不可加，则包装为容器列
    If funcBody.hasBoundaryParenthesis Then
        Set funcBody = SqlFormatterHelper.wrapperColToContainerCol(funcBody)
    End If

    ' 保存元素
    funcBody.addComponent c
End Sub

' 将元素添加到开头位置
Public Sub ISqlFormatterElem_addComponentToFirst(ByRef c As ISqlFormatterElem)
    ' noop
End Sub

' 能否将元素添加到开头位置
Public Function ISqlFormatterElem_canAddComponentToFirst() As Boolean
    ' return false
End Function

' ==================================================
' ISqlFormatterElem -- 对齐处理
' ==================================================
' 该方法是用来配置对齐的方法
' 返回元素内，最多一级深度下的对齐元素组
' 如果是列对象，则返回作用域各个组成元素内的条件组（主要是子查询）
' 如果是条件对象，则返回条件自身
' 如果是作用域对象，则返回作用域内各部分中的条件组合
' 如果是赋值语句，则返回语句自身
' 如果没有条件或者赋值语句，则返回 null
'
' @return IObjArrayRef<IObjArrayRef<ISqlFormatterAlignmentElem>>
Public Function ISqlFormatterElem_getAlignmentElemGroups() As IObjArrayRef
    If funcBody Is Nothing Then
        ' return null
        Exit Function
    End If

    Set ISqlFormatterElem_getAlignmentElemGroups = funcBody.getAlignmentElemGroups
End Function

' ==================================================
' ISqlFormatterElem -- 前缀空格设置
' ==================================================
' 设置当前元素的嵌套深度，并计算前缀空格的数量
' 会覆盖 setPrefixSpaceCount 的设置结果
Public Sub ISqlFormatterElem_setElemNestCount(ByVal nestCount As Integer, ByRef config As SqlFormatterConfiguration)
    prefixSpaceCount = nestCount * config.elemNestLevelPrefixSpaceCount

    ' 函数体和函数本身是同一级别
    If Not funcBody Is Nothing Then
        Dim subNestCount As Integer
        subNestCount = nestCount
        ' 如果存在边界括号，则增加一个单位
        If ISqlFormatterElem_hasBoundaryParenthesis Then
            subNestCount = subNestCount + 1
        End If

        funcBody.setElemNestCount subNestCount, config
    End If
End Sub

' 直接设置前缀空格的数量
' 将会覆盖 setElemNestCount 对前缀空格数量的控制
Public Sub ISqlFormatterElem_setPrefixSpaceCount(ByVal spaceCount As Integer, ByRef config As SqlFormatterConfiguration)
    prefixSpaceCount = spaceCount

    ' 函数体和函数本身是同一级别
    If Not funcBody Is Nothing Then
        Dim subSpaceCount As Integer
        subSpaceCount = spaceCount
        ' 如果存在边界括号，则增加一个单位
        If ISqlFormatterElem_hasBoundaryParenthesis Then
            subSpaceCount = subSpaceCount + config.elemNestLevelPrefixSpaceCount
        End If

        funcBody.setPrefixSpaceCount subSpaceCount, config
    End If
End Sub

' ==================================================
' ISqlFormatterElem -- 边界括号
' ==================================================
' 是否存在边界括号，有左边或者右边都返回 true
Public Function ISqlFormatterElem_hasBoundaryParenthesis() As Boolean
    If leftParenthesisCount > 0 Then
        ISqlFormatterElem_hasBoundaryParenthesis = True
    ElseIf rightParenthesisCount > 0 Then
        ISqlFormatterElem_hasBoundaryParenthesis = True
    End If

    ' return False
End Function

' 边界处的 [(] 的数量 + count 个
Public Sub ISqlFormatterElem_addBoundaryLeftParenthesis(Optional ByVal count As Integer = 1)
    leftParenthesisCount = leftParenthesisCount + count
End Sub

' 边界处的 [)] 的数量 + count 个
Public Sub ISqlFormatterElem_addBoundaryRightParenthesis(Optional ByVal count As Integer = 1)
    rightParenthesisCount = rightParenthesisCount + count
End Sub

' ==================================================
' ISqlFormatterElem -- 内容检查
' ==================================================
' 获取类型
Public Function ISqlFormatterElem_getType() As SqlTokenType
    ISqlFormatterElem_getType = SqlTokenType.STR_FUNC
End Function

' 检查当前元素是不是一个注释
Public Function ISqlFormatterElem_isComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是和前一个元素处于同一行的注释
Public Function ISqlFormatterElem_isShareLineComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是处于单独一行的注释
Public Function ISqlFormatterElem_isSingleLineComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是以注释结尾
Public Function ISqlFormatterElem_isEndsWithComment() As Boolean
    ' 1. 如果存在右括号，则默认不是以注释结尾
    If rightParenthesisCount > 0 Then
        ' return false;
        Exit Function
    End If

    ' 2. 否则检查函数体
    If funcBody Is Nothing Then
        ' return false
        Exit Function
    End if

    ISqlFormatterElem_isEndsWithComment = funcBody.isEndsWithComment
End Function

' 检查当前生成器是不是多行的
Public Function ISqlFormatterElem_isMultiLine() As Boolean
    If funcBody Is Nothing Then
        ' return false
        Exit Function
    End If

    ' 1. 检查: 如果函数体是多行的，则返回 true
    If funcBody.isMultiLine Then
        ' return true
        ISqlFormatterElem_isMultiLine = True
        Exit Function
    End If

    ' 2. 否则，检查: 如果函数体是以注释结尾的，则继续
    If funcBody.isEndsWithComment Then
        ' 2.1. 检查: 如果当前对象内存在边界括号，则返回 true
        If leftParenthesisCount > 0 Then
            ' return true
            ISqlFormatterElem_isMultiLine = True
            Exit Function
        End If

        If rightParenthesisCount > 0 Then
            ' return true
            ISqlFormatterElem_isMultiLine = True
            Exit Function
        End If
    End If

    ' 3. 返回 false
    ' return false
End Function

' ==================================================
' ISqlFormatterElem -- 生成列的字符串
' ==================================================
' 生成一整行的字符串。如果本身是多行，将会使用换行符进行连接
Public Function ISqlFormatterElem_toString(ByRef config As SqlFormatterConfiguration) As String
    If ISqlFormatterElem_isMultiLine = False Then
        ' 1. 如果是单行的，则直接生成单行字符串
        ISqlFormatterElem_toString = doCreateSingleLineString(config)
        Exit Function
    Else
        ' 1. 生成原始字符集合
        Dim arrRef As IPrimitiveArrayRef
        Set arrRef = doCreateMultiLineArray(config)
        ' 2. 使用换行符连接
        ISqlFormatterElem_toString = UtilsArrayRef.reducePrimitiveArrayRefToStr(arrRef, config.lineBreak)
        Exit Function
    End If
End Function

' 生成多行字符串
' 如果只有一行，则返回的数组引用中将会只有一个
' 并且不会在开头附加前缀空格
'
' @return IPrimitiveArrayRef<String>
Public Function ISqlFormatterElem_toStringArray(ByRef config As SqlFormatterConfiguration) As IPrimitiveArrayRef
    ' ==================================================
    ' 1. 处理单行的情况
    ' ==================================================
    ' 1. 检查: 如果是单行的，则生成单行字符串
    If ISqlFormatterElem_isMultiLine = False Then
        Dim singleStr As String
        singleStr = doCreateSingleLineString(config)

        ' 2. 将 `singleStr` 包装成一个长度为 1 的结果集合，并返回
        Set ISqlFormatterElem_toStringArray = UtilsArrayRef.wrapperToPrimitiveArrayRef(singleStr)
        Exit Function
    End If

    ' ==================================================
    ' 2. 处理多行的情况
    ' ==================================================
    ' 1. 生成多行字符串集合，返回生成结果
    Set ISqlFormatterElem_toStringArray = doCreateMultiLineArray(config)
End Function

' ======================================================================
' Private Function
' ======================================================================
' 生成函数列的单行字符串
Private Function doCreateSingleLineString(ByRef config As SqlFormatterConfiguration) As String
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果字符串 `result = ""`
    Dim result As String
    ' 2. 临时元素 `ISqlFormatterElem component`
    Dim component As ISqlFormatterElem
    ' 3. 遍历索引 `long i`
    Dim i As Long
    ' 4. 临时字符串 `String temp`
    Dim temp As String

    ' ==================================================
    ' 2. 处理左边界括号
    ' ==================================================
    ' 1. 检查: 如果 `leftParenthesisCount > 0`，则继续
    If leftParenthesisCount > 0 Then
        ' 2. 生成等量的空格，并保存到 `result += UtilsString.repeatLeftParenthesis(leftParenthesisCount)`
        result = result + UtilsString.repeatLeftParenthesis(leftParenthesisCount)
    End If

    ' ==================================================
    ' 3. 处理函数名
    ' ==================================================
    ' 1. 连接 `result += funcNameToken.value`
    result = result & funcNameToken.value

    ' ==================================================
    ' 4. 处理函数体
    ' ==================================================
    ' 1. 检查: 如果函数体存在，则继续
    If Not funcBody Is Nothing Then
        ' 2. 生成函数体的单行字符串，保存到 `temp`: `result += funcBody.toString(config)`
        temp = funcBody.toString(config)

        ' 3. 检查: 如果 `temp` 的第一个字符不是 `(`，则在 `result` 后增加一个空格
        If Left(temp, 1) <> "(" Then
            result = result & " "
        End If

        ' 4. 将 `temp` 连接到 result 后面
        result = result & temp
    End If

    ' ==================================================
    ' 5. 处理右边界括号
    ' ==================================================
    ' 1. 检查: 如果 `rightParenthesisCount > 0`，则继续
    If rightParenthesisCount > 0 Then
        ' 2. 生成等量的空格，并保存到 `result += UtilsString.repeatRightParenthesis(rightParenthesisCount)`
        result = result & UtilsString.repeatRightParenthesis(rightParenthesisCount)
    End If

    ' ==================================================
    ' 6. 退出处理，并返回 `result`
    ' ==================================================
    doCreateSingleLineString = result
End Function

' 生成函数列的多行字符串集合
' @return IPrimitiveArrayRef<String>
Private Function doCreateMultiLineArray(ByRef config As SqlFormatterConfiguration) As IPrimitiveArrayRef
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 前缀空格 `String prefixSpaceStr = ""`
    Dim prefixSpaceStr As String
    ' 2. 结果字符串集合 `IPrimitiveArrayRef<String> result`
    '    这里直接使用 doCreateMultiLineArray
    ' 3. 遍历索引 `long i`
    Dim i As Long
    ' 4. 函数体的行数 `long funcBodyLineCount`
    Dim funcBodyLineCount As Long
    ' 5. 行字符串 `String lineStr`
    Dim lineStr As String
    ' 6. 元素的字符串集合 `IPrimitiveArrayRef<String> tempArray`
    Dim tempArray As IPrimitiveArrayRef
    ' 7. 临时字符串 `String temp`
    Dim temp As String

    ' ==================================================
    ' 2. 处理函数名
    ' ==================================================
    ' 1. 连接函数名: `lineStr = funcNameToken.value`
    lineStr = funcNameToken.value

    ' ==================================================
    ' 3. 处理不存在边界括号的情况
    ' ==================================================
    ' 1. 检查: 如果当前列对象不存在边界括号，则继续
    If ISqlFormatterElem_hasBoundaryParenthesis = False Then

        ' 2. 处理函数体
        ' 2.1. 获取函数体的原始字符串集合 `tempArray = funcBody.toStringArray(config)`
        Set tempArray = funcBody.toStringArray(config)

        ' 2.2. 将函数体第一行保存到 `temp`
        temp = tempArray.Item(0)

        ' 2.3. 检查: 如果 `temp` 的第一个字符不是 `(`，在 `lineStr` 后面附加一个空格
        If Left(temp, 1) <> "(" Then
            lineStr = lineStr & " "
        End If

        ' 2.4. 将 `temp` 连接到 `lineStr` 后面
        lineStr = lineStr & temp

        ' 2.5. 将 `lineStr` 保存到 `tempArray` 的第一行
        tempArray.setVal 0, lineStr

        ' 2.6. 设置 `result = tempArray`
        Set doCreateMultiLineArray = tempArray

    ' ==================================================
    ' 4. 处理存在边界括号的情况
    ' ==================================================
    Else
        ' ==============================
        ' 4.1. 处理前缀空格
        ' ==============================
        ' 1. 生成一个单位的前缀空格 `prefixSpaceStr = config.elemNestLevelPrefixSpace`
        prefixSpaceStr = config.elemNestLevelPrefixSpace

        ' ==============================
        ' 4.2. 处理函数体
        ' ==============================
        ' 1. 获取函数体的原始字符串集合 `tempArray = funcBody.toStringArray(config)`
        Set tempArray = funcBody.toStringArray(config)
        ' 2. 保存函数体的行数: `funcBodyLineCount = tempArray.Count`
        funcBodyLineCount = tempArray.Count

        ' ==============================
        ' 4.3. 初始化结果集合
        ' ==============================
        ' 1. 检查: 如果存在前缀空格，则创建 `result = UtilsArrayRef.createRealPrimitiveArrayRef<ISqlFormatterElem>(funcBodyLineCount + 2)`
        '    2 表示: 边界括号 2 行
        Set doCreateMultiLineArray = UtilsArrayRef.createRealPrimitiveArrayRef(funcBodyLineCount + 2)

        ' ==============================
        ' 4.4. 处理左边界括号
        ' ==============================
        ' 1. 如果存在左边界括号，则继续: `leftParenthesisCount > 0`
        If leftParenthesisCount > 0 Then
            ' 2. 生成等量的左括号，并保存到 `result`
            doCreateMultiLineArray.Add UtilsString.repeatLeftParenthesis(leftParenthesisCount)
        End If

        ' ==============================
        ' 4.5. 连接函数名
        ' ==============================
        ' 1. 将函数体第一行保存到 `temp`
        temp = tempArray.Item(0)
        ' 2. 检查: 如果 `temp` 的第一个字符不是 `(`，在 `lineStr` 后面附加一个空格
        If Left(temp, 1) <> "(" Then
            lineStr = lineStr & " "
        End If

        ' 3. 将 `temp` 连接到 `lineStr` 后面
        lineStr = lineStr & temp
        ' 4. 将 `prefixSpaceStr + lineStr` 保存到 `result`
        doCreateMultiLineArray.Add prefixSpaceStr & lineStr

        ' ==============================
        ' 4.6. 保存函数体
        ' ==============================
        ' 1. 从 `i = 1` 开始到 `funcBodyLineCount - 1` 遍历 `tempArray`
        With tempArray ' ---- 在这里加速
            For i = 1 To funcBodyLineCount - 1
                ' 2. 将 `prefixSpaceStr + tempArray 的每一行` 保存到 `result`
                doCreateMultiLineArray.Add prefixSpaceStr & .Item(i)
            Next
        End With

        ' ==============================
        ' 4.7. 处理右边界括号
        ' ==============================
        ' 1. 如果存在右边界括号，则继续: `rightParenthesisCount > 0`
        If rightParenthesisCount > 0 Then
            ' 2. 生成等量的右括号，并保存到 `result`
            doCreateMultiLineArray.Add UtilsString.repeatRightParenthesis(rightParenthesisCount)
        End If

    End if

    ' ==================================================
    ' 5. 退出处理，并返回 `result`
    ' ==================================================
    ' return result;
End Function